/*
 * Copyright (C) 2011 Young, Fey <fey.young@gmail.com>
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

package com.youngfey.util
import scala.actors._
import Actor.State._

/**
 * @author Young, Fey <fey.young@gmail.com>
 */
abstract class Hydra(val observer: HydraObserver, val threads: Int)
  extends Actor {
  /**
   * override this funtion to define how to get an HydraOutput from a msg
   */
  def run(msg: HydraMessage): HydraOutput

  var hydraTimeout = 1000
  var engineTimeout = 1000
  var waitTime = 1000
  private val engines: Array[Engine] = new Array(threads)
  for(i <- 0 until threads) { engines(i) = new Engine(this, engineTimeout) }

  var idle = true

  protected def handle(msg: HydraMessage) {
    idle = false
    getState match {
      case New => start
      case Terminated => restart
      case _ =>
    }
    this ! msg
  }

  def act() {
    engines.foreach { _.start }
    var done = false
    while(!done) {
      receiveWithin(hydraTimeout) {
        case msg: HydraMessage => 
          val idleEngine = engines.find(!_.busy)
          if(idleEngine.isDefined) idleEngine.get ! msg else {
            Thread.sleep(waitTime)
            handle(msg)
          }
        case output: HydraOutput => observer.update(output)
        case failure: HydraFailure => observer.recover(failure)
        case TIMEOUT =>
          if(!(false /: engines) { _ || _.busy }) done = true
        case _ =>
      }
    }
    idle = true
  }
}
